package cn.iocoder.yudao.module.nginxui.service.httpconfig;

import cn.iocoder.yudao.module.nginxui.dal.dataobject.config.NginxConfigDO;
import cn.iocoder.yudao.module.nginxui.dal.dataobject.httpconfig.HttpConfigDO;

import cn.iocoder.yudao.module.nginxui.dal.dataobject.httplocation.HttpLocationDO;
import cn.iocoder.yudao.module.nginxui.dal.dataobject.httpserver.HttpServerDO;
import cn.iocoder.yudao.module.nginxui.dal.mysql.httpconfig.HttpConfigMapper;
import cn.iocoder.yudao.module.nginxui.dal.mysql.httplocation.HttpLocationMapper;
import cn.iocoder.yudao.module.nginxui.dal.mysql.httpserver.HttpServerMapper;
import cn.iocoder.yudao.module.nginxui.service.config.NginxConfigService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;

import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static cn.iocoder.yudao.module.nginxui.util.StringUtils.hasText;


/**
 * HTTP配置同步服务：将三级配置同步到nginxui_config.content
 * Location操作 → 关联Server → 关联HTTP → 关联主配置ID → 同步到nginxui_config.content
 * Server操作 → 关联HTTP → 关联主配置ID → 同步到nginxui_config.content
 * HTTP操作 → 直接关联主配置ID → 同步到nginxui_config.content
 */
@Service
public class HttpConfigSyncService {

    @Resource
    private HttpConfigMapper httpConfigMapper;
    @Resource
    private HttpServerMapper httpServerMapper;
    @Resource
    private HttpLocationMapper httpLocationMapper;
    @Resource
    private NginxConfigService nginxConfigService;

    /**
     * 当HTTP/Server/Location配置变更时，同步更新主配置
     * @param configId 主配置ID（nginxui_config.id）
     */
    @Transactional(rollbackFor = Exception.class)
    public void syncToMainConfig(Long configId) {
        // 1. 获取主配置
        NginxConfigDO mainConfig = nginxConfigService.getConfig(configId);
        if (mainConfig == null) {
            return;
        }

        // 2. 先清除所有已存在的HTTP块（包括默认的）
        String content = clearHttpBlock(mainConfig.getContent());

        // 3. 获取当前生效的HTTP配置
        HttpConfigDO httpConfig = httpConfigMapper.selectByConfigIdAndStatus(configId, 1);
        if (httpConfig == null) {
            // 无生效配置，直接保存清除后的内容
            nginxConfigService.updateConfigContent(configId, content);
            return;
        }

        // 4. 构建新HTTP块并插入
        String httpBlock = buildHttpBlock(httpConfig);
        String newContent = insertHttpBlock(content, httpBlock);

        // 5. 更新主配置
        nginxConfigService.updateConfigContent(configId, newContent);
    }

    /**
     * 构建完整的HTTP块内容（包含所有Server和Location）
     */
    private String buildHttpBlock(HttpConfigDO httpConfig) {
        StringBuilder httpContent = new StringBuilder();
        httpContent.append("# HTTP模块配置（自动生成）\n");
        httpContent.append("http {\n");

        // 1. HTTP级基础配置
        httpContent.append("    # MIME类型配置\n");
        httpContent.append("    include /etc/nginx/mime.types;\n");
        httpContent.append("    default_type application/octet-stream;\n\n");

        // 日志配置
        if (hasText(httpConfig.getLogFormat())) {
            httpContent.append("    # 日志格式配置\n");
            httpContent.append("    log_format main '").append(httpConfig.getLogFormat()).append("';\n");
            httpContent.append("    access_log /var/log/nginx/access.log main;\n\n");
        }

        // sendfile配置
        int sendfile = httpConfig.getSendfile() != null ? httpConfig.getSendfile() : 1;
        httpContent.append("    # sendfile机制\n");
        httpContent.append("    sendfile ").append(sendfile == 1 ? "on" : "off").append(";\n\n");

        // 连接超时配置
        int keepaliveTimeout = httpConfig.getKeepaliveTimeout() != null ? httpConfig.getKeepaliveTimeout() : 65;
        httpContent.append("    # 长连接超时时间\n");
        httpContent.append("    keepalive_timeout ").append(keepaliveTimeout).append(";\n\n");

        // 客户端最大请求体
        String clientMaxBodySize = hasText(httpConfig.getClientMaxBodySize())
                ? httpConfig.getClientMaxBodySize()
                : "1m";
        httpContent.append("    # 客户端最大请求体大小\n");
        httpContent.append("    client_max_body_size ").append(clientMaxBodySize).append(";\n\n");

        // 额外HTTP配置
        if (hasText(httpConfig.getExtraHttpConfig())) {
            httpContent.append("    # 额外HTTP配置\n");
            httpContent.append("    ").append(httpConfig.getExtraHttpConfig().replace("\n", "\n    ")).append("\n\n");
        }

        // 2. 添加所有Server块（按sort排序）
        List<HttpServerDO> servers = httpServerMapper.selectByHttpIdAndStatus(httpConfig.getId(), 1);
        if (servers != null && !servers.isEmpty()) {
            servers.sort(Comparator.comparingInt(HttpServerDO::getSort)); // 按排序号升序
            for (HttpServerDO server : servers) {
                httpContent.append(buildServerBlock(server));
            }
        }

        httpContent.append("}");
        return httpContent.toString();
    }

    /**
     * 构建Server块内容（包含所有Location）
     */
    private String buildServerBlock(HttpServerDO server) {
        StringBuilder serverContent = new StringBuilder();
        serverContent.append("    # Server配置：").append(server.getServerName()).append("\n");
        serverContent.append("    server {\n");

        // Server基础配置
        serverContent.append("        listen ").append(server.getListen()).append(";\n");
        serverContent.append("        server_name ").append(server.getServerName()).append(";\n");

        if (hasText(server.getRoot())) {
            serverContent.append("        root ").append(server.getRoot()).append(";\n");
        }
        serverContent.append("        index ").append(server.getIndexFile()).append(";\n");

        // SSL配置
        if (hasText(server.getSslCertificate())) {
            serverContent.append("        ssl_certificate ").append(server.getSslCertificate()).append(";\n");
            serverContent.append("        ssl_certificate_key ").append(server.getSslCertificateKey()).append(";\n");
        }

        // 额外Server配置
        if (hasText(server.getExtraServerConfig())) {
            serverContent.append("\n        # 额外Server配置\n");
            serverContent.append("        ").append(server.getExtraServerConfig().replace("\n", "\n        ")).append("\n");
        }

        // 添加所有Location块（按sort排序）
        List<HttpLocationDO> locations = httpLocationMapper.selectByServerIdAndStatus(server.getId(), 1);
        if (locations != null && !locations.isEmpty()) {
            locations.sort(Comparator.comparingInt(HttpLocationDO::getSort)); // 按排序号升序（Nginx匹配顺序关键）
            for (HttpLocationDO location : locations) {
                serverContent.append(buildLocationBlock(location));
            }
        }

        serverContent.append("    }\n\n");
        return serverContent.toString();
    }

    /**
     * 构建Location块内容
     */
    private String buildLocationBlock(HttpLocationDO location) {
        StringBuilder locationContent = new StringBuilder();
        locationContent.append("        # Location配置：").append(location.getPath()).append("\n");
        locationContent.append("        location ").append(location.getPath()).append(" {\n");

        // Location基础配置
        if (hasText(location.getRoot())) {
            locationContent.append("            root ").append(location.getRoot()).append(";\n");
        }
        if (hasText(location.getProxyPass())) {
            locationContent.append("            proxy_pass ").append(location.getProxyPass()).append(";\n");
        }
        if (hasText(location.getTryFiles())) {
            locationContent.append("            try_files ").append(location.getTryFiles()).append(";\n");
        }
        if (hasText(location.getRewrite())) {
            locationContent.append("            rewrite ").append(location.getRewrite()).append(";\n");
        }
        if (hasText(location.getAllow())) {
            locationContent.append("            allow ").append(location.getAllow()).append(";\n");
        }
        if (hasText(location.getDeny())) {
            locationContent.append("            deny ").append(location.getDeny()).append(";\n");
        }

        // 额外Location配置
        if (hasText(location.getExtraLocationConfig())) {
            locationContent.append("\n            # 额外Location配置\n");
            locationContent.append("            ").append(location.getExtraLocationConfig().replace("\n", "\n            ")).append("\n");
        }

        locationContent.append("        }\n\n");
        return locationContent.toString();
    }

    /**
     * 清除主配置中已有的HTTP块（包含所有子块）
     */
    private String clearHttpBlock(String content) {
        if (content == null) {
            return "";
        }
        // 关键修改：匹配所有HTTP块（无论是否有注释）
        // (?s) 表示单行模式，.可以匹配换行符；.*? 是非贪婪匹配，避免匹配到最后一个}
        Pattern pattern = Pattern.compile(
                "(?s)\\s*#?\\s*HTTP.*?http\\s*\\{.*?}\\s*",
                Pattern.CASE_INSENSITIVE
        );
        Matcher matcher = pattern.matcher(content);
        String processed = matcher.replaceAll("");

        // 二次清理：处理可能残留的孤立HTTP块（无注释的情况）
        Pattern bareHttpPattern = Pattern.compile(
                "(?s)\\s*http\\s*\\{.*?}\\s*",
                Pattern.CASE_INSENSITIVE
        );
        processed = bareHttpPattern.matcher(processed).replaceAll("");

        // 清理空行
        return processed.replaceAll("(?m)^\\s*$\\n?", "").trim() + "\n";
    }

    /**
     * 将新构建的HTTP块插入到主配置中（标准位置：events块之后）
     */
    private String insertHttpBlock(String content, String httpBlock) {
        if (content == null || content.trim().isEmpty()) {
            return httpBlock;
        }

        // 优先插入到events块之后（Nginx标准顺序）
        Pattern eventsPattern = Pattern.compile("(\\s*)events\\s*\\{.*?}\\s*",
                Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
        Matcher eventsMatcher = eventsPattern.matcher(content);
        if (eventsMatcher.find()) {
            return eventsMatcher.replaceFirst(
                    eventsMatcher.group() + "\n\n" +  // 保留原events块并加空行
                            httpBlock                          // 插入HTTP块
            );
        }

        // 其次插入到全局配置之后
        Pattern globalPattern = Pattern.compile("(?s)(^.*?(worker_processes|error_log|pid).*?;\\s*)(\\n|$)",
                Pattern.CASE_INSENSITIVE);
        Matcher globalMatcher = globalPattern.matcher(content);
        if (globalMatcher.find()) {
            return globalMatcher.replaceFirst("$1\n\n" + httpBlock + "\n");
        }

        // 都匹配不到时，添加到文件末尾
        return content + "\n\n" + httpBlock;
    }
}
